package com.szcinda.service.dto.calculatePrice;

import com.szcinda.repository.*;
import com.szcinda.service.TypeStringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Transactional
public class CalculateServiceImpl implements CalculateService {

    private final LinePriceRepository linePriceRepository;
    private final CarrierRepository carrierRepository;
    private final ClientVehiclePriceRepository clientVehiclePriceRepository;

    public CalculateServiceImpl(LinePriceRepository linePriceRepository, CarrierRepository carrierRepository, ClientVehiclePriceRepository clientVehiclePriceRepository) {
        this.linePriceRepository = linePriceRepository;
        this.carrierRepository = carrierRepository;
        this.clientVehiclePriceRepository = clientVehiclePriceRepository;
    }

    @Override
    public List<PriceDto> calculate(CalculateDto calculateDto) {
        List<PriceDto> priceDtos = new ArrayList<>();
        if (calculateDto.getCalculateTime() == null) {
            return priceDtos;
        }
        // 国际班列按照班列日期进行计费
        // 综合物流按照提货日期
        List<LinePrice> clientPriceList = linePriceRepository.findByBelongId(calculateDto.getClientId());
        // 普通计费方式
        List<LinePrice> clientPrices = clientPriceList.stream().filter(linePrice -> linePrice.getBusinessType().equals(calculateDto.getBusinessCode()) &&
                linePrice.getCalculateType().equals(calculateDto.getCalculateType()) && linePrice.getOriginPlace().equals(calculateDto.getOriginPlace()) &&
                linePrice.getDestPlace().equals(calculateDto.getDestPlace()) &&
                // 线路有效期间前后加一天进行判断
                linePrice.getStartDate().minusDays(1).isBefore(calculateDto.getCalculateTime()) &&
                linePrice.getEndDate().plusDays(1).isAfter(calculateDto.getCalculateTime())
        ).collect(Collectors.toList());
        clientPrices.forEach(linePrice -> {
            if (calculateDto.isGhOrder()) {
                // 综合物流需要算两次，一次体积，一次重量
                // 1、体积报价为0或空值，重量报价有值，计算毛重*重量报价  和  体积重*重量报价，取大值
                // 2、体积报价不为0  重量报价不为0  计算 体积*体积报价   毛重*重量报价  取大值
                // 3、体积报价不为0  重量报价为0，计算体积*体积报价 唯一值
                this.generatePrice(linePrice, priceDtos, calculateDto);
            } else {
                // 国际班列的计费逻辑
                PriceDto dto = calculate(calculateDto, linePrice);
                dto.setType(TypeStringUtils.feeInType);
                priceDtos.add(dto);
            }
        });
        // 综合物流需要根据毛重乘以单价和体积乘以单价比较，哪个大取哪个
        if (calculateDto.isGhOrder()) {
            // 先按照费用科目排序，然后拿分组里面费用最高的
            Map<String, List<PriceDto>> listMap = priceDtos.stream().collect(Collectors.groupingBy(PriceDto::getFeeName));
            // 清空报价记录，重新填充
            priceDtos.clear();
            listMap.forEach((feeName, priceDtoList) -> {
                // 列表排序
                Collections.sort(priceDtoList);
                // 把最大的一条加到集合
                priceDtos.add(priceDtoList.get(0));
            });
        }
        // 判断是否是综合物流的费用
        if (calculateDto.isGhOrder()) {
            // 需要计算提货费用和派送费用
            List<ClientVehiclePrice> vehiclePrices = clientVehiclePriceRepository.findByBelongId(calculateDto.getClientId());
            if (StringUtils.hasText(calculateDto.getVehicleType()) && StringUtils.hasText(calculateDto.getVehicleLine())) {
                vehiclePrices.stream().filter(clientVehiclePrice -> calculateDto.getVehicleLine().startsWith(clientVehiclePrice.getStartPlace()) &&
                        calculateDto.getVehicleLine().endsWith(clientVehiclePrice.getEndPlace()) && clientVehiclePrice.getName().equals(calculateDto.getVehicleType()))
                        .findFirst()
                        .ifPresent(clientVehiclePrice -> {
                            PriceDto dto = new PriceDto();
                            dto.setType(TypeStringUtils.feeInType);
                            dto.setFeeName("提货费");
                            dto.setCurrency(TypeStringUtils.RMB);
                            dto.setFeeCount(clientVehiclePrice.getAmount());
                            priceDtos.add(dto);
                        });
            }
            if (StringUtils.hasText(calculateDto.getDeliveryModel()) && StringUtils.hasText(calculateDto.getDeliveryLine())) {
                vehiclePrices.stream().filter(clientVehiclePrice -> calculateDto.getDeliveryLine().startsWith(clientVehiclePrice.getStartPlace()) &&
                        calculateDto.getDeliveryLine().endsWith(clientVehiclePrice.getEndPlace()) && clientVehiclePrice.getName().equals(calculateDto.getDeliveryModel()))
                        .findFirst()
                        .ifPresent(clientVehiclePrice -> {
                            PriceDto dto = new PriceDto();
                            dto.setType(TypeStringUtils.feeInType);
                            dto.setFeeName("派送费");
                            dto.setCurrency(TypeStringUtils.RMB);
                            dto.setFeeCount(clientVehiclePrice.getAmount());
                            priceDtos.add(dto);
                        });
            }
        }
        return priceDtos;
    }

    private void generatePrice(LinePrice linePrice, List<PriceDto> priceDtos, CalculateDto calculateDto) {
        if (CalculateType.FIXED.equals(calculateDto.getCalculateType()) || CalculateType.QUANTITY.equals(calculateDto.getCalculateType())) {
            PriceDto dtoW = calculate(calculateDto, linePrice);
            dtoW.setType(TypeStringUtils.feeInType);
            priceDtos.add(dtoW);
            return;
        }
        if (linePrice.getPrice() != null && linePrice.getPrice2() != null && linePrice.getPrice() > 0 && linePrice.getPrice2() > 0) {
            // 2、体积报价不为0  重量报价不为0  计算 体积*体积报价   毛重*重量报价  取大值
            CalculateDto newCalculator = new CalculateDto();
            BeanUtils.copyProperties(calculateDto, newCalculator);
            newCalculator.setCalculateType(CalculateType.VOLUME);
            PriceDto dtoV = calculate(newCalculator, linePrice);
            dtoV.setType(TypeStringUtils.feeInType);
            priceDtos.add(dtoV);
            newCalculator.setCalculateType(CalculateType.WEIGHT);
            PriceDto dtoW = calculate(newCalculator, linePrice);
            dtoW.setType(TypeStringUtils.feeInType);
            priceDtos.add(dtoW);
        } else if ((linePrice.getPrice() == null || linePrice.getPrice() == 0) && linePrice.getPrice2() != null && linePrice.getPrice2() > 0) {
            // 3、体积报价不为0  重量报价为0，计算体积*体积报价 唯一值
            CalculateDto newCalculator = new CalculateDto();
            BeanUtils.copyProperties(calculateDto, newCalculator);
            newCalculator.setCalculateType(CalculateType.VOLUME);
            PriceDto dtoW = calculate(newCalculator, linePrice);
            dtoW.setType(TypeStringUtils.feeInType);
            priceDtos.add(dtoW);
        } else if ((linePrice.getPrice2() == null || linePrice.getPrice2() == 0) && linePrice.getPrice() != null && linePrice.getPrice() > 0) {
            // 1、体积报价为0或空值，重量报价有值，计算毛重*重量报价  和  体积重*重量报价，取大值
            CalculateDto newCalculator = new CalculateDto();
            BeanUtils.copyProperties(calculateDto, newCalculator);
            newCalculator.setCalculateType(CalculateType.WEIGHT);
            PriceDto dtoW = calculate(newCalculator, linePrice);
            dtoW.setType(TypeStringUtils.feeInType);
            priceDtos.add(dtoW);
            // 体积重
            newCalculator = new CalculateDto();
            BeanUtils.copyProperties(calculateDto, newCalculator);
            newCalculator.setCalculateType(CalculateType.WEIGHT);
            newCalculator.setGrossWeight(newCalculator.getVolumeWeight());
            PriceDto dtoW2 = calculate(newCalculator, linePrice);
            dtoW2.setType(TypeStringUtils.feeInType);
            priceDtos.add(dtoW2);
        }
    }

    @Override
    public List<PriceDto> calculateOut(CalculateDto calculateDto) {
        List<PriceDto> priceDtos = new ArrayList<>();
        if (calculateDto.getCalculateTime() == null) {
            return priceDtos;
        }
        // 国际班列按照班列日期进行计费
        // 综合物流按照提货日期
        Carrier carrier = carrierRepository.findByOrganizationIdAndName(calculateDto.getOrganizationId(), calculateDto.getCarrierId());
        if (carrier == null) {
            return priceDtos;
        }
        List<LinePrice> carrierPriceList = linePriceRepository.findByBelongId(carrier.getId());
        List<LinePrice> carrierPrices = carrierPriceList.stream().filter(linePrice -> linePrice.getBusinessType().equals(calculateDto.getBusinessCode()) &&
                linePrice.getCalculateType().equals(calculateDto.getCalculateType()) && linePrice.getOriginPlace().equals(calculateDto.getOriginPlace()) &&
                linePrice.getDestPlace().equals(calculateDto.getDestPlace()) &&
                // 线路有效期间前后加一天进行判断
                linePrice.getStartDate().minusDays(1).isBefore(calculateDto.getCalculateTime()) &&
                linePrice.getEndDate().plusDays(1).isAfter(calculateDto.getCalculateTime())
        ).collect(Collectors.toList());
        if (!calculateDto.isNoTips()) {
            Assert.isTrue(carrierPrices.size() > 0, String.format("供应商【%s】没有找到相同业务类型、计费方式、起始站、目的站，并且在有效期范围内的线路报价", carrier.getName()));
        }
        //  过滤科目
        List<LinePrice> linePrices = carrierPrices.stream().filter(lp -> lp.getFeeName().equals(calculateDto.getFeeName())).collect(Collectors.toList());
        for (LinePrice linePrice : linePrices) {
            if (calculateDto.isGhOrder()) {
                // 综合物流需要算两次，一次体积，一次重量
                // 1、体积报价为0或空值，重量报价有值，计算毛重*重量报价  和  体积重*重量报价，取大值
                // 2、体积报价不为0  重量报价不为0  计算 体积*体积报价   毛重*重量报价  取大值
                // 3、体积报价不为0  重量报价为0，计算体积*体积报价 唯一值
                this.generatePrice(linePrice, priceDtos, calculateDto);
            } else {
                // 正常国际版列计费逻辑
                PriceDto dto = calculateOut(calculateDto, linePrice);
                dto.setType(TypeStringUtils.feeOutType);
                priceDtos.add(dto);
            }
        }
        // 去除重复，取最大的一条
        if (priceDtos.size() > 0) {
            Map<String, List<PriceDto>> listMap = priceDtos.stream().collect(Collectors.groupingBy(PriceDto::getFeeName));
            // 清空报价记录，重新填充
            priceDtos.clear();
            listMap.forEach((feeName, priceDtoList) -> {
                // 列表排序
                Collections.sort(priceDtoList);
                // 把最大的一条加到集合
                priceDtos.add(priceDtoList.get(0));
            });
        }
        Assert.isTrue(priceDtos.size() > 0 || calculateDto.isNoTips(), String.format("供应商【%s】没有维护费用科目【%s】的线路报价", carrier.getName(), calculateDto.getFeeName()));
        return priceDtos;
    }

    @Override
    public List<PriceDto> calculateVehicleFee(CalculateDto calculateDto) {
        List<PriceDto> priceDtos = new ArrayList<>();
        List<Carrier> carriers = carrierRepository.findByOrganizationId(calculateDto.getOrganizationId());
        Assert.isTrue(carriers != null && carriers.stream().anyMatch(carrier -> carrier.getName().equals(calculateDto.getCarrierId())), String.format("当前机构下找不到供应商【%s】", calculateDto.getCarrierId()));
        carriers.stream().filter(carrier -> carrier.getName().equals(calculateDto.getCarrierId()))
                .findFirst()
                .ifPresent(carrier -> {
                    // 找出供应商的车型费用
                    List<ClientVehiclePrice> clientVehiclePrices = clientVehiclePriceRepository.findByBelongId(carrier.getId());
                    // 提货费
                    Optional<ClientVehiclePrice> vehiclePrice = clientVehiclePrices.stream().filter(clientVehiclePrice -> clientVehiclePrice.getName().equals(calculateDto.getVehicleType()) &&
                            calculateDto.getVehicleLine() != null && calculateDto.getVehicleLine().startsWith(clientVehiclePrice.getStartPlace()) &&
                            calculateDto.getVehicleLine().endsWith(clientVehiclePrice.getEndPlace()))
                            .findFirst();
                    PriceDto priceDto = new PriceDto();
                    if (vehiclePrice.isPresent()) {
                        priceDto.setFeeCount(vehiclePrice.get().getAmount());
                        priceDto.setCurrency("人民币");
                    } else {
                        priceDto.setFeeCount(0);
                        priceDto.setCurrency(null);
                    }
                    priceDto.setFeeName("提货费");
                    priceDto.setType("OUT");
                    priceDtos.add(priceDto);
                    // 派送费
                    vehiclePrice = clientVehiclePrices.stream().filter(clientVehiclePrice -> clientVehiclePrice.getName().equals(calculateDto.getDeliveryModel()) &&
                            calculateDto.getDeliveryLine() != null && calculateDto.getDeliveryLine().startsWith(clientVehiclePrice.getStartPlace()) &&
                            calculateDto.getDeliveryLine().endsWith(clientVehiclePrice.getEndPlace()))
                            .findFirst();
                    priceDto = new PriceDto();
                    if (vehiclePrice.isPresent()) {
                        priceDto.setFeeCount(vehiclePrice.get().getAmount());
                        priceDto.setCurrency("人民币");
                    } else {
                        priceDto.setFeeCount(0);
                        priceDto.setCurrency(null);
                    }
                    priceDto.setFeeName("派送费");
                    priceDto.setType("OUT");
                    priceDtos.add(priceDto);
                });
        return priceDtos;
    }

    private PriceDto calculateOut(CalculateDto calculateDto, LinePrice linePrice) {
        PriceDto priceDto = new PriceDto();
        priceDto.setFeeName(linePrice.getFeeName());
        priceDto.setCurrency(linePrice.getCurrency());
        switch (calculateDto.getCalculateType()) {
            case FIXED:
                priceDto.setFeeCount(linePrice.getPrice());
                break;
            case VOLUME:
                BigDecimal b1;
                if (!calculateDto.isGhOrder()) {
                    b1 = BigDecimal.valueOf(linePrice.getPrice());
                } else {
                    b1 = BigDecimal.valueOf(linePrice.getPrice2());
                }
                BigDecimal b2 = BigDecimal.valueOf(calculateDto.getSquareNumber());
                priceDto.setFeeCount(BigDecimal.valueOf(b1.multiply(b2).doubleValue()).setScale(2, RoundingMode.HALF_UP).doubleValue());
                break;
            case WEIGHT:
                BigDecimal b3 = BigDecimal.valueOf(linePrice.getPrice());
                double weight = calculateDto.getGrossWeight();
                BigDecimal b4 = BigDecimal.valueOf(weight);
                priceDto.setFeeCount(BigDecimal.valueOf(b3.multiply(b4).doubleValue()).setScale(2, RoundingMode.HALF_UP).doubleValue());
                break;
            case QUANTITY:
                BigDecimal b5 = BigDecimal.valueOf(linePrice.getPrice());
                BigDecimal b6 = BigDecimal.valueOf(calculateDto.getQuantity());
                priceDto.setFeeCount(BigDecimal.valueOf(b5.multiply(b6).doubleValue()).setScale(2, RoundingMode.HALF_UP).doubleValue());
                break;
        }
        return priceDto;
    }

    private PriceDto calculate(CalculateDto calculateDto, LinePrice linePrice) {
        PriceDto priceDto = new PriceDto();
        priceDto.setFeeName(linePrice.getFeeName());
        priceDto.setCurrency(linePrice.getCurrency());
        switch (calculateDto.getCalculateType()) {
            case FIXED:
                priceDto.setFeeCount(linePrice.getPrice());
                break;
            case VOLUME:
                BigDecimal b1;
                if (!calculateDto.isGhOrder()) {
                    b1 = BigDecimal.valueOf(linePrice.getPrice());
                } else {
                    b1 = BigDecimal.valueOf(linePrice.getPrice2());
                }
                BigDecimal b2 = BigDecimal.valueOf(calculateDto.getSquareNumber());
                priceDto.setFeeCount(BigDecimal.valueOf(b1.multiply(b2).doubleValue()).setScale(2, RoundingMode.HALF_UP).doubleValue());
                break;
            case WEIGHT:
                BigDecimal b3 = BigDecimal.valueOf(linePrice.getPrice());
                double weight = calculateDto.getGrossWeight();
                BigDecimal b4 = BigDecimal.valueOf(weight);
                priceDto.setFeeCount(BigDecimal.valueOf(b3.multiply(b4).doubleValue()).setScale(2, RoundingMode.HALF_UP).doubleValue());
                break;
            case QUANTITY:
                BigDecimal b5 = BigDecimal.valueOf(linePrice.getPrice());
                if (calculateDto.getType() == 1) {
                    // 国际班列标志，单独的计算逻辑
                    priceDto.setFeeCount(BigDecimal.valueOf(b5.doubleValue()).setScale(2, RoundingMode.HALF_UP).doubleValue());
                } else {
                    BigDecimal b6 = BigDecimal.valueOf(calculateDto.getQuantity());
                    priceDto.setFeeCount(BigDecimal.valueOf(b5.multiply(b6).doubleValue()).setScale(2, RoundingMode.HALF_UP).doubleValue());
                }
                break;
        }
        return priceDto;
    }
}
